package glmodel;

import java.io.*;
import java.util.ArrayList;
import java.util.StringTokenizer;
import ui.Message;
import glapp.*;

/**
 * Loads a library of materials from a .mtl file into an array of GLMaterial
 * objects.
 */
public class GLMaterialLib
{
	// path to the .mtl file (loadMaterial() will set these)
	// we'll load texture images from same folder as material file
	public String	filepath	= "";
	public String	filename	= "";

	// array of materials loaded from .mtl file
	GLMaterial[]	materials;

	public GLMaterialLib(String mtlFilename)
	{
		if (mtlFilename != null && mtlFilename.length() > 0)
		{
			materials = loadMaterials(mtlFilename);
		}
	}

	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	 * functions to load and save materials (from/to .mtl file) * * * * * * * *
	 * * * * * * * * * * * * * * * * * * * * * * * *
	 */

	public GLMaterial[] loadMaterials(String mtlFilename)
	{
		GLMaterial[] mtls = null;
		// Separate leading path from filename (we'll load textures from same folder)
		String[] pathParts = GLApp.getPathAndFile(mtlFilename);
		filepath = pathParts[0];
		filename = pathParts[1];
		// read the mtl file
		try
		{
			mtls = loadMaterials(new BufferedReader(new InputStreamReader(GLApp.getInputStream(mtlFilename))));
		}
		catch (Exception e)
		{
			Message.getInstance().msg("GLMaterialLib.loadMaterials(): Exception when loading " + mtlFilename + ": " + e);
		}
		return mtls;
	}

	/**
	 * MTL file format in a nutshell:
	 * 
	 * <PRE>
	 * 		newmtl white          // begin material and specify name
	 * 		Kd 1.0 1.0 1.0        // diffuse rgb
	 * 		Ka 0.2 0.2 0.2        // ambient rgb
	 * 		Ks 0.6 0.6 0.6        // specular rgb
	 * 		Ns 300                // shininess 0-1000
	 *      d 0.5                 // alpha 0-1
	 *      map_Kd texture.jpg    // texture file
	 *                            // blank line ends material definition
	 * </PRE>
	 */
	public GLMaterial[] loadMaterials(BufferedReader br)
	{
		ArrayList<GLMaterial> mtlslist = new ArrayList<GLMaterial>();
		GLMaterial material = null;
		String line = "";

		float[] rgb;
		try
		{
			while ((line = br.readLine()) != null)
			{
				// remove extra whitespace
				line = line.trim();
				if (line.length() > 0)
				{
					if (line.startsWith("#"))
					{
						// ignore comments
					}
					else if (line.startsWith("newmtl"))
					{
						// newmtl some_name
						material = new GLMaterial(); // start new material
						material.setName(line.substring(7));
						mtlslist.add(material); // add to list
					}
					else if (line.startsWith("Kd"))
					{
						// Kd 1.0 0.0 0.5
						if ((rgb = read3Floats(line)) != null)
						{
							material.setDiffuse(rgb);
						}
					}
					else if (line.startsWith("Ka"))
					{
						// Ka 1.0 0.0 0.5
						if ((rgb = read3Floats(line)) != null)
						{
							material.setAmbient(rgb);
						}
					}
					else if (line.startsWith("Ks"))
					{
						// Ks 1.0 0.0 0.5
						if ((rgb = read3Floats(line)) != null)
						{
							material.setSpecular(rgb);
						}
					}
					else if (line.startsWith("Ns"))
					{
						// Ns 500.5
						// shininess in mtl file is 0-1000
						if ((rgb = read3Floats(line)) != null)
						{
							// convert to opengl 0-127
							int shininessValue = (int) ((rgb[0] / 1000f) * 127f);
							material.setShininess(shininessValue);
						}
					}
					else if (line.startsWith("d"))
					{
						// d 1.0
						// alpha value of material 0=transparent 1=opaque
						if ((rgb = read3Floats(line)) != null)
						{
							material.setAlpha(rgb[0]);
						}
					}
					else if (line.startsWith("illum"))
					{
						// illum (0, 1, or 2)
						// lighting for material 0=disable, 1=ambient & diffuse (specular is black), 2 for full lighting.
						if ((rgb = read3Floats(line)) != null)
						{
							// not yet
						}
					}
					else if (line.startsWith("map_Kd"))
					{
						// map_Kd filename
						// add a texture to the material
						String textureFile = line.substring(7);
						if (textureFile != null && !textureFile.equals(""))
						{
							int textureHandle = 0;
							try
							{
								textureHandle = GLApp.makeTexture(filepath + textureFile);
							}
							catch (Exception e)
							{
								Message.getInstance().msg("GLMaterialLib.loadMaterials(): could not load texture file (" + line + ")" + e);
							}
							material.setTextureFile(textureFile);
							material.setTexture(textureHandle);
						}
					}
				}
			}
		}
		catch (Exception e)
		{
			Message.getInstance().msg("GLMaterialLib.loadMaterials() failed at line: " + line);
		}
		// debug:
		Message.getInstance().msg("GLMaterialLib.loadMaterials(): loaded " + mtlslist.size() + " materials ");
		// return array of materials
		GLMaterial[] mtls = new GLMaterial[mtlslist.size()];
		mtlslist.toArray(mtls);
		return mtls;
	}

	// always return array of four floats (usually containing RGBA, but
	// in some cases contains only one value at pos 0).
	private float[] read3Floats(String line)
	{
		try
		{
			StringTokenizer st = new StringTokenizer(line, " ");
			st.nextToken(); // throw out line identifier (Ka, Kd, etc.)
			if (st.countTokens() == 1)
			{
				return new float[]{Float.parseFloat(st.nextToken()), 0f, 0f, 0f};
			}
			else if (st.countTokens() == 3)
			{ // RGBA (force A to 1)
				return new float[]{Float.parseFloat(st.nextToken()), Float.parseFloat(st.nextToken()), Float.parseFloat(st.nextToken()), 1f};
			}
		}
		catch (Exception e)
		{
			Message.getInstance().msg("GLMaterialLib.read3Floats(): error on line '" + line + "', " + e);
		}
		return null;
	}

	

	/**
	 * find a material by name in an array of GLMaterial objects return the
	 * array index of the material
	 */
	public int findID(String materialName)
	{
		if (materials != null && materialName != null)
		{
			for (int m = 0; m < materials.length; m++)
			{
				if (materials[m].mtlname.equals(materialName))
				{
					return m;
				}
			}
		}
		return -1;
	}

}